#include <drivers/spi.h>
#include <kernel/fdt.h>


struct st7789v {
    struct spi_device *spi;
    int gpio_rst;
    int gpio_dc;
    void *buffer;
};


static void st7789v_delay(int delay_ms)
{
    for(volatile int i = 0; i < delay_ms; i++) {
        for(volatile int j = 0; j < 10; j++);;
    }
}


void spi_write_byte(struct spi_device *spi, uint8_t data)
{
    struct spi_master *master = spi->master;
    struct spi_transfer xfer;
    
    xfer.len = 1;
    xfer.tx_buf = &data;
    xfer.rx_buf = NULL;

    master->transfer(spi, &xfer);
}


void st7789v_write_cmd(struct spi_device *spi, uint8_t cmd)
{
    struct st7789v *display = spi_get_drvdata(spi);

    gpio_set_value(display->gpio_dc, 0); // 设置PA0为低电平
    spi_write_byte(spi, cmd);
}


void st7789v_write_data(struct spi_device *spi, uint8_t data)
{
    struct st7789v *display = spi_get_drvdata(spi);

    gpio_set_value(display->gpio_dc, 1); // 设置PA0为高电平
    spi_write_byte(spi, data);
}

void st7789v_set_win(struct spi_device *spi, uint16_t xStar, uint16_t yStar, uint16_t xEnd, uint16_t yEnd)
{
    struct st7789v *display = spi_get_drvdata(spi);

    st7789v_write_cmd(spi, 0x2a);
    st7789v_write_data(spi, (xStar >> 8) & 0xff);
	st7789v_write_data(spi, xStar & 0xff);
    st7789v_write_data(spi, (xEnd >> 8) & 0xff);
	st7789v_write_data(spi, xEnd & 0xff);
    st7789v_write_cmd(spi, 0x2b); 
    st7789v_write_data(spi, (yStar >> 8) & 0xff);
	st7789v_write_data(spi, yStar & 0xff );
    st7789v_write_data(spi, (yEnd >> 8) & 0xff);
	st7789v_write_data(spi, yEnd & 0xff);
    st7789v_write_cmd(spi, 0x2c);
    gpio_set_value(display->gpio_dc, 1);
}


void st7789v_init(struct spi_device *spi, int rotation)
{
    /* reset display */
    struct st7789v *display = spi_get_drvdata(spi);
    
    gpio_set_value(display->gpio_rst, 0);
    st7789v_delay(2000);
    gpio_set_value(display->gpio_rst, 1);
    st7789v_delay(2000);

    st7789v_write_cmd(spi, 0x11);
    st7789v_delay(100);
	st7789v_write_cmd(spi, 0x3A);
	st7789v_write_data(spi, 0x05);
	st7789v_write_cmd(spi, 0xc5);
	st7789v_write_data(spi, 0x1a);
	st7789v_write_cmd(spi, 0x36); //rotation
	if(rotation == 0) {
		st7789v_write_data(spi, 0x00);
	}
	else if(rotation == 90) {
		st7789v_write_data(spi, 0xc0);
	}
	else if(rotation == 180) {
		st7789v_write_data(spi, 0x70);
	}
	else if(rotation == 270){
		st7789v_write_data(spi, 0xa0);
	}

//-------------ST7789V Frame rate setting-----------//
	st7789v_write_cmd(spi, 0xb2);
	st7789v_write_data(spi, 0x05);
	st7789v_write_data(spi, 0x05);
	st7789v_write_data(spi, 0x00);
	st7789v_write_data(spi, 0x33);
	st7789v_write_data(spi, 0x33);
	st7789v_write_cmd(spi, 0xb7);
	st7789v_write_data(spi, 0x35);
//--------------ST7789V Power setting---------------//
	st7789v_write_cmd(spi, 0xbb);
	st7789v_write_data(spi, 0x3f);
	st7789v_write_cmd(spi, 0xc0);
	st7789v_write_data(spi, 0x2c);
	st7789v_write_cmd(spi, 0xc2);
	st7789v_write_data(spi, 0x01);
	st7789v_write_cmd(spi, 0xc3);
	st7789v_write_data(spi, 0x0f);
	st7789v_write_cmd(spi, 0xc4);
	st7789v_write_data(spi, 0x20);
	st7789v_write_cmd(spi, 0xc6);
	st7789v_write_data(spi, 0x11);
	st7789v_write_cmd(spi, 0xd0);
	st7789v_write_data(spi, 0xa4);
	st7789v_write_data(spi, 0xa1);
	st7789v_write_cmd(spi, 0xe8);
	st7789v_write_data(spi, 0x03);
	st7789v_write_cmd(spi, 0xe9);
	st7789v_write_data(spi, 0x09);
	st7789v_write_data(spi, 0x09);
	st7789v_write_data(spi, 0x08);
	st7789v_write_cmd(spi, 0xe0);
	st7789v_write_data(spi, 0xd0);
	st7789v_write_data(spi, 0x05);
	st7789v_write_data(spi, 0x09);	
	st7789v_write_data(spi, 0x09);
	st7789v_write_data(spi, 0x08);
	st7789v_write_data(spi, 0x14);	
	st7789v_write_data(spi, 0x28);
	st7789v_write_data(spi, 0x33);
	st7789v_write_data(spi, 0x3f);
	st7789v_write_data(spi, 0x07);
	st7789v_write_data(spi, 0x13);
	st7789v_write_data(spi, 0x14);
	st7789v_write_data(spi, 0x28);
	st7789v_write_data(spi, 0x30);	
	st7789v_write_cmd(spi, 0xe1);
	st7789v_write_data(spi, 0xd0);
	st7789v_write_data(spi, 0x05);
	st7789v_write_data(spi, 0x09);	
	st7789v_write_data(spi, 0x09);
	st7789v_write_data(spi, 0x08);
	st7789v_write_data(spi, 0x03);	
	st7789v_write_data(spi, 0x24);
	st7789v_write_data(spi, 0x32);
	st7789v_write_data(spi, 0x32);
	st7789v_write_data(spi, 0x3b);
	st7789v_write_data(spi, 0x14);
	st7789v_write_data(spi, 0x13);
	st7789v_write_data(spi, 0x28);
	st7789v_write_data(spi, 0x2f);
	st7789v_write_cmd(spi, 0x21);
	st7789v_write_cmd(spi, 0x11);
    st7789v_delay(120);      //Delay 120ms
    st7789v_write_cmd(spi, 0x29); //display turn on

    // flush display
    st7789v_set_win(spi, 0, 0, 239, 239);
    spi_set_speed(spi, 50000000);
    for(int i = 0; i < 240 * 240; i++) {
        st7789v_write_data(spi, 0x09);
        st7789v_write_data(spi, 0xf7);
    }
}


static int st7789v_probe(struct spi_device *dev)
{
    int ret = 0;
    size_t gpio_rst, gpio_dc;
    fdt_node_t *spi_node = dev->device.dt_node;

    struct st7789v *display = kmalloc(sizeof(struct st7789v));
    display->spi = dev;

    ret = fdt_read_prop_int(spi_node, "gpio-reset", &gpio_rst);
    if(ret) {
        SYSLOG_ERROR("gpio-reset not found");
        return -1;
    }

    ret = gpio_request(gpio_rst);
    if(ret) {
        return -1;
    }

    ret = fdt_read_prop_int(spi_node, "gpio-dc", &gpio_dc);
    if(ret) {
        SYSLOG_ERROR("gpio-dc not found");
        return -1;
    }

    ret = gpio_request(gpio_dc);
    if(ret) {
        return -1;
    }

    display->gpio_rst = gpio_rst;
    display->gpio_dc = gpio_dc;
    gpio_direction_output(gpio_rst, IO_LOW);
    gpio_direction_output(gpio_dc, IO_HIGH);

    spi_set_drvdata(dev, display);

    struct spi_master *master = dev->master;
    dev->speed = 1000000;
    master->setup(dev);

    st7789v_init(dev, 0);

    return 0;
}


static void st7789v_remove(struct spi_device *dev)
{
    SPI_DEBUG_TRACE("st7789v_remove");
}


static struct of_device_id st7789v_match[] = {
    {.compatible = "sitronix,st7789v", },
    { }
};


struct spi_driver st7789v_driver = {
    .driver = {
        .name = "st7789v_driver",
        .of_match_table = st7789v_match,
    },
    .probe = st7789v_probe,
    .remove = st7789v_remove,
};


static int st7789v_driver_register()
{
    return spi_driver_register(&st7789v_driver);
}


driver_initcall(st7789v_driver_register);